#include "cinder/app/AppBasic.h"
#include "cinder/Surface.h"
#include "cinder/gl/Texture.h"
#include "cinder/qtime/QuickTime.h"
#include "cinder/Text.h"
#include "cinder/Utilities.h"
#include "cinder/ImageIo.h"
#include "QDir"
#include "JackControl.h"
#include "cinder/Camera.h"
#include "cinder/params/Params.h"

#include "cinder/gl/Light.h"
#include "cinder/gl/Material.h"
#include "cinder/MayaCamUI.h"

using namespace cinder;
using namespace cinder::app;
using namespace std;

GLfloat no_mat[]			= { 0.0, 0.0, 0.0, 1.0 };
ci::ColorA cNoMat			= ci::ColorA( 0.0f, 0.0f, 0.0f, 1.0f );

GLfloat mat_ambient[]		= { 0.5, 0.5, 0.5, 1.0 };
ci::ColorA cAmbient			= ci::ColorA( 0.5f, 0.5f, 0.5f, 0.5f );

GLfloat mat_diffuse[]		= { 0.3, 0.5, 0.8, 1.0 };
ci::ColorA cDiffuse			= ci::ColorA( 0.3f, 0.5f, 0.8f, 0.5f );

GLfloat mat_specular[]		= { 1.0, 1.0, 1.0, 1.0 };
ci::ColorA cSpecular		= ci::ColorA( 1.0f, 1.0f, 1.0f, 0.5f );

GLfloat mat_emission[]		= { 0.0, 0.1, 0.3, 0.0 };
ci::ColorA cEmission		= ci::ColorA( 0.0f, 0.1f, 0.3f, 0.0f );

GLfloat mat_shininess[]		= { 128.0 };
GLfloat no_shininess[]		= { 0.0 };

class HyperTerrainVis : public AppBasic {
public:
    void setup();
    // Mouse
            void mouseMove( MouseEvent event );
            void mouseDown( MouseEvent event );
            void mouseDrag( MouseEvent event );
            void mouseUp  ( MouseEvent event );
    void keyDown( KeyEvent event );
    void update();
    void draw();
    void prepareSettings( Settings *settings );

    float* xs;
    float* ys;
    float* zs;
    float* os;
    int orbitI;
    int frames;
    JackControl* jackControl;
    CameraPersp mCam;
    Vec3f mEye,mCenter,mUp;
    MayaCamUI				mMayaCam;
    ci::CameraPersp			*_cameraPerspective;
    ci::Quatf				*_sceneRotation;
    params::InterfaceGl mParams;
    Quatf mSceneRotation;
    // mat
    bool DIFFUSE;
    bool AMBIENT;
    bool SPECULAR;
    bool EMISSIVE;
    // Light
    gl::Light			*_light;
    gl::Material		*_ribbonMaterial;

    Vec2f			mMousePos;
};

void HyperTerrainVis::prepareSettings( Settings *settings ){
    settings->setWindowSize( 1280, 1024 );
    settings->setFrameRate( 80.0f );
}

void HyperTerrainVis::setup()
{
    setFullScreen( true );
    //mCam.setPerspective( 60.0f, getWindowAspectRatio(), 5.0f, 3000.0f );
    //mEye        = Vec3f( 0.0f, 0.0f, 500.0f );
    //mCenter     = Vec3f::zero();
    //mUp         = Vec3f::yAxis();

    //mCam.lookAt( mEye, mCenter, mUp );
    //gl::setMatrices( mCam );
    //mSceneRotation = Quatf(0.000,0.0,0.00005,0.0f);
    //gl::rotate( mSceneRotation );

    // Create camera
            Vec3f p = Vec3f::one() * 10.0f;
            CameraPersp cam = CameraPersp( getWindowWidth(), getWindowHeight(), 45.0f );
            cam.setEyePoint( p );
            cam.setCenterOfInterestPoint( Vec3f::zero() );
            cam.setPerspective( 45.0f, getWindowAspectRatio(), 0.1f, 500.0f );

            // Set mayacamera
            mMayaCam.setCurrentCam( cam );

    DIFFUSE = true;
    AMBIENT = true;
    SPECULAR = true;
    EMISSIVE = true;

    jackControl = new JackControl();
    xs = new float[(int)jackControl->getFrames()*2];
    ys = new float[(int)jackControl->getFrames()*2];
    zs = new float[(int)jackControl->getFrames()*2];
    os = new float[(int)jackControl->getFrames()*2];
    frames = (int)jackControl->getFrames();
    orbitI = 0;

    // set up our projector (a special type of light)
            _light = new gl::Light( gl::Light::POINT, 0 );
            //mLight = new gl::Light( gl::Light::DIRECTIONAL, 0 );
            _light->lookAt( ci::Vec3f::one() * 20, Vec3f( 0, 0, 0 ) );
            _light->setAmbient( Color( 1.0f, 1.0f, 1.0f ) );
            _light->setDiffuse( Color( 1.0f, 1.0f, 1.0f ) );
            _light->setSpecular( Color( 1.0f, 1.0f, 1.0f ) );
            _light->setShadowParams( 40.0f, 1.0f, 30.0f );
            _light->enable();

            // setup our scene (a simple gray cube)
            _ribbonMaterial = new gl::Material();
            _ribbonMaterial->setSpecular( Color::white() );
            _ribbonMaterial->setDiffuse( Color(0.5f, 0.5f, 0.5f) );
            _ribbonMaterial->setAmbient( Color( 0.5f, 0.5f, 0.5f ) );
            _ribbonMaterial->setShininess( 5.0f );
}

void HyperTerrainVis::mouseMove( MouseEvent event )
{
        mMousePos.x = event.getX() - getWindowWidth() * 0.5f;
        mMousePos.y = getWindowHeight() * 0.5f - event.getY();
}

void HyperTerrainVis::mouseDown( MouseEvent event )
{
        // let the camera handle the interaction
        mMayaCam.mouseDown( event.getPos() );
}

void HyperTerrainVis::mouseDrag( MouseEvent event )
{
        // let the camera handle the interaction
        mMayaCam.mouseDrag( event.getPos(), event.isLeftDown(), event.isMetaDown(), event.isRightDown() );
}

void HyperTerrainVis::mouseUp( MouseEvent event )
{
        // let the camera handle the interaction
        mMayaCam.mouseDown( event.getPos() );
}

void HyperTerrainVis::keyDown( KeyEvent event )
{
    // Change full screen with 'f'
    if( event.getChar() == 'f' ) {
        setFullScreen( ! isFullScreen() );
    }
    //	_isOptionDown = event.isMetaDown();

            if( event.getChar() == 'd' || event.getChar() == 'd' ){
                    DIFFUSE = ! DIFFUSE;
            }
            else if( event.getChar() == 'a' || event.getChar() == 'A' ){
                    AMBIENT = ! AMBIENT;
            }
            else if( event.getChar() == 's' || event.getChar() == 'S' ){
                    SPECULAR = ! SPECULAR;
            }
            else if( event.getChar() == 'e' || event.getChar() == 'E' ){
                    EMISSIVE = ! EMISSIVE;
            }
            //else if( event.getChar() == 'f' || event.getChar() == 'F' ){
            //        setFullScreen( ! isFullScreen() );
            //}
            else if( event.getChar() == '/' || event.getChar() == '?' ){
    //		mInfoPanel.toggleState();
            }
            else if( event.getChar() == 'r' || event.getChar() == 'R' ){
    //		renderInfoPanel = ! renderInfoPanel;
            }
            else if( event.getChar() == ',' || event.getChar() == '<' ){
                    mat_shininess[0] *= 0.5f;
                    if( mat_shininess[0] < 8.0f )
                            mat_shininess[0] = 8.0f;
            }
            else if( event.getChar() == '.' || event.getChar() == '>' ){
                    mat_shininess[0] *= 2.0f;
                    if( mat_shininess[0] > 128.0f )
                            mat_shininess[0] = 128.0f;
            }
    }

void HyperTerrainVis::update()
{
    for(int i=0;i<jackControl->getFrames();i++) {
        xs[orbitI] = jackControl->getInput1()[i];
        ys[orbitI] = jackControl->getInput2()[i];
        zs[orbitI] = jackControl->getInput3()[i];
        os[orbitI] = jackControl->getInput4()[i];
        orbitI = (orbitI+1)%(jackControl->getFrames()*1);
    }
}

void HyperTerrainVis::draw()
{
    //gl::clear( Color( 0, 0, 0 ) );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    gl::enableDepthWrite();
    gl::enableDepthRead();
    gl::enableAlphaBlending();
    //glMaterialfv(GL_FRONT_FACE,GL_SHININESS,mat_shininess);
    //glMaterialfv(GL_FRONT_FACE,GL_AMBIENT,mat_ambient);
    //glMaterialfv(GL_FRONT_FACE,GL_DIFFUSE,mat_diffuse);
    //glMaterialfv(GL_FRONT_FACE,GL_SPECULAR,mat_specular);
    //glMaterialfv(GL_FRONT_FACE,GL_EMISSION,mat_emission);

    //glEnable( GL_LIGHTING );
    //glEnable( GL_LIGHT0 );

    //glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    //GLfloat light_position[] = { mMousePos.x, mMousePos.y, 75.0f, 0.0f };
    //glLightfv( GL_LIGHT0, GL_POSITION, light_position );
    //_ribbonMaterial->apply();

    /*
    //1
    glVertex3f(
        Vec3f(
            ((this->getWindowWidth() * 0.5)* xs[0])+ (this->getWindowWidth() * 0.5) -size,
            ((this->getWindowHeight() * 0.5)* ys[0])+ (this->getWindowHeight() * 0.5) -size,
            zs[0]*360+30 + size
        )
    );

    //2
    glVertex3f(
        Vec3f(
            ((this->getWindowWidth() * 0.5)* xs[skip])+ (this->getWindowWidth() * 0.5) -skip,
            ((this->getWindowHeight() * 0.5)* ys[skip])+ (this->getWindowHeight() * 0.5) +size,
            zs[skip]*360+30 + size
        )
    );
    */
    glBegin(GL_LINE_STRIP);
    int skip=1;


            for(int i=0;i<(jackControl->getFrames() -skip);i+=skip) {
                if(!((xs[i]==0 && ys[i]==0) || (xs[i+skip]==0 && ys[i+skip]==0))) {
                    int size = (2*os[i])+4;
                    Vec3f i0p = Vec3f(
                                ((this->getWindowWidth() * 0.5)* xs[i])+ (this->getWindowWidth() * 0.5),
                                ((this->getWindowHeight() * 0.5)* ys[i])+ (this->getWindowHeight() * 0.5),
                                zs[i]*360
                            );
                    Vec3f i1p = Vec3f(
                                ((this->getWindowWidth() * 0.5)* xs[i+skip])+ (this->getWindowWidth() * 0.5),
                                ((this->getWindowHeight() * 0.5)* ys[i+skip])+ (this->getWindowHeight() * 0.5),
                                zs[i+skip]*360
                            );
                    Vec3f perpV = Vec3f(
                                ( ((this->getWindowWidth() * 0.5)* xs[i])+ (this->getWindowWidth() * 0.5)),
                                (((this->getWindowHeight() * 0.5)* ys[i])+ (this->getWindowHeight() * 0.5)) -size,
                                (zs[i]*360)
                            );
                    perpV = perpV - i0p;
                    perpV = perpV.normalized();
                    i0p = i1p-i0p;
                    i0p = i0p.normalized();

                    Vec3f axis0 = i0p.cross(perpV);
                    axis0 = axis0.normalized();
                    double angle0 = acos(i0p.dot(perpV));

                    //Single Line Above method
                    /*
                    Vec3f p = Vec3f(
                        ( ((this->getWindowWidth() * 0.5)* xs[i])+ (this->getWindowWidth() * 0.5)),
                        (((this->getWindowHeight() * 0.5)* ys[i])+ (this->getWindowHeight() * 0.5)) -10,
                        (zs[i]*360)
                    );

                    p.rotate(axis0,angle0*0.01);

                    glVertex3f(
                                Vec3f(
                                    ( ((this->getWindowWidth() * 0.5)* xs[i])+ (this->getWindowWidth() * 0.5)),
                                    (((this->getWindowHeight() * 0.5)* ys[i])+ (this->getWindowHeight() * 0.5)),
                                    (zs[i]*360)
                                )
                    );
                    glVertex3f(
                        p
                    );

                    glVertex3f(
                                Vec3f(
                                    ( ((this->getWindowWidth() * 0.5)* xs[i])+ (this->getWindowWidth() * 0.5)),
                                    (((this->getWindowHeight() * 0.5)* ys[i])+ (this->getWindowHeight() * 0.5)),
                                    (zs[i]*360)
                                )
                    );
                    */


                    //1
                    Vec3f p1 = Vec3f(
                        ( ((this->getWindowWidth() * 0.5)* xs[i])+ (this->getWindowWidth() * 0.5)) -size,
                        (((this->getWindowHeight() * 0.5)* ys[i])+ (this->getWindowHeight() * 0.5)),
                        (zs[i]*360) +size
                    );

                    //2
                    Vec3f p2 = Vec3f(
                        ( ((this->getWindowWidth() * 0.5)* xs[i+skip])+ (this->getWindowWidth() * 0.5)) -size,
                        (((this->getWindowHeight() * 0.5)* ys[i+skip])+ (this->getWindowHeight() * 0.5)),
                        (zs[i]*360) +size
                    );

                    //3
                    Vec3f p3 = Vec3f(
                        ( ((this->getWindowWidth() * 0.5)* xs[i+skip])+ (this->getWindowWidth() * 0.5)) +size,
                        (((this->getWindowHeight() * 0.5)* ys[i+skip])+ (this->getWindowHeight() * 0.5)),
                        (zs[i]*360) +size
                    );

                    //4
                    Vec3f p4 = Vec3f(
                        ( ((this->getWindowWidth() * 0.5)* xs[i])+ (this->getWindowWidth() * 0.5)) +size,
                        (((this->getWindowHeight() * 0.5)* ys[i])+ (this->getWindowHeight() * 0.5)),
                        (zs[i]*360) +size
                    );

                    //5
                    Vec3f p5 = Vec3f(
                        ( ((this->getWindowWidth() * 0.5)* xs[i])+ (this->getWindowWidth() * 0.5)) -size,
                        (((this->getWindowHeight() * 0.5)* ys[i])+ (this->getWindowHeight() * 0.5)),
                        (zs[i]*360) -size
                    );

                    //6
                    Vec3f p6 = Vec3f(
                        ( ((this->getWindowWidth() * 0.5)* xs[i+skip])+ (this->getWindowWidth() * 0.5)) -size,
                        (((this->getWindowHeight() * 0.5)* ys[i+skip])+ (this->getWindowHeight() * 0.5)),
                        (zs[i]*360) -size
                    );

                    //7
                    Vec3f p7 = Vec3f(
                        ( ((this->getWindowWidth() * 0.5)* xs[i+skip])+ (this->getWindowWidth() * 0.5)) +size,
                        (((this->getWindowHeight() * 0.5)* ys[i+skip])+ (this->getWindowHeight() * 0.5)),
                        (zs[i]*360) -size
                    );

                    //8
                    Vec3f p8 = Vec3f(
                        ( ((this->getWindowWidth() * 0.5)* xs[i])+ (this->getWindowWidth() * 0.5)) +size,
                        (((this->getWindowHeight() * 0.5)* ys[i])+ (this->getWindowHeight() * 0.5)),
                        (zs[i]*360) -size
                    );

                    //Line Boxes
                    glVertex3f(p1);
                    glVertex3f(p5);
                    glVertex3f(p1);

                    glVertex3f(p2);
                    glVertex3f(p6);
                    glVertex3f(p2);

                    glVertex3f(p3);
                    glVertex3f(p7);
                    glVertex3f(p3);

                    glVertex3f(p4);
                    glVertex3f(p8);
                    glVertex3f(p4);

                    glVertex3f(p1);
                    glVertex3f(p5);
                    glVertex3f(p8);
                    glVertex3f(p7);
                    glVertex3f(p6);
                    glVertex3f(p5);
                    glVertex3f(p6);
                    glVertex3f(p2);

                    /*
                    //Quads
                    glVertex3f(p1);
                    glVertex3f(p2);

                    glVertex3f(p3);
                    glVertex3f(p2);

                    glVertex3f(p7);
                    glVertex3f(p8);

                    glVertex3f(p6);
                    glVertex3f(p5);

                    glVertex3f(p2);
                    glVertex3f(p1);
                    */
                }
            }
    glEnd();
    //Quatf rot0 = Quatf( axis0.normalized(), angle0 );
    //cinder::Matrix44f mat0 = cinder::Matrix44f::createRotation(axis0, angle0 );
}

CINDER_APP_BASIC( HyperTerrainVis, RendererGl )
